//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Delete point from polygon/polyline.
//

# include "StdAfx.h"
# include "DeletePoint.h"
# include "UndoRedo.h"
# include "Label.h"
# include "MapView.h"

static
point_extra_t _GetPointExtra (rgn_t * _pRgn, size_t _cElement, size_t _cPointIndex) {
	const size_t cElements = _pRgn->elements.size ();
	if (_cElement < cElements) {
		rgn_element_t & element = _pRgn->elements [_cElement];
		if (element.pExtra) {
			point_extras_t & PointExtras = element.pExtra->PointExtras;
			if (_cPointIndex < PointExtras.size ())
				return PointExtras [_cPointIndex];
		}
	}
	return point_extra_t ();
}

class CDeletePointAction : public CEditAction {
  public:
	CDeletePointAction (rgn_t * _pRgn, size_t _cElement, size_t _cPointIndex) :
		m_pRgn (_pRgn),
		m_cElement (_cElement),
		m_cPointIndex (_cPointIndex),
		m_point (_pRgn->elements [_cElement].points [_cPointIndex]),
		m_pointExtra (_GetPointExtra (_pRgn, _cElement, _cPointIndex)),
		m_bWasModified ((_pRgn->dwFlags & rgn_t::fModified) != 0)
	{}

	virtual void GetName (string_t & _str) const;
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	rgn_t *       const m_pRgn;
	size_t        const m_cElement;
	size_t        const m_cPointIndex;

	point_t       const m_point;
	point_extra_t const m_pointExtra;

	bool          const m_bWasModified;
};

void CDeletePointAction::GetName (string_t & _str) const {
	_str.append ("delete point [");
	GetHintText (* m_pRgn, _str);
	_str.append ("]");
}

bool CDeletePointAction::Do () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	assert (m_cElement < m_pRgn->elements.size ());
	rgn_element_t & element = m_pRgn->elements [m_cElement];

	if (element.pExtra != NULL)
		element.ReleaseRGNodes ();

	points_t & points = element.points;
	const size_t cPrevSize = points.size ();
	points.erase (points.begin () + m_cPointIndex);

	if (element.pExtra != NULL) {
		point_extras_t & PointExtras = element.pExtra->PointExtras;
		if (! PointExtras.empty ()) {
			PointExtras.resize (cPrevSize);
			PointExtras.erase (PointExtras.begin () + m_cPointIndex);
			assert (PointExtras.size () == points.size ());
		}

		element.AddRefRGNodes ();
	}

	m_pRgn->RefreshAttributes (g_map);
	m_pRgn->dwFlags |= rgn_t::fModified;

	return true;
}

void CDeletePointAction::Undo () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	assert (m_cElement < m_pRgn->elements.size ());
	rgn_element_t & element = m_pRgn->elements [m_cElement];

	if (element.pExtra != NULL)
		element.ReleaseRGNodes ();

	points_t & points = element.points;
	const size_t cPrevSize = points.size ();
	points.insert (points.begin () + m_cPointIndex, m_point);

	if (element.pExtra != NULL) {
		point_extras_t & PointExtras = element.pExtra->PointExtras;
		if (! PointExtras.empty ()) {
			PointExtras.resize (cPrevSize);
			PointExtras.insert (PointExtras.begin () + m_cPointIndex, m_pointExtra);
			assert (PointExtras.size () == points.size ());
		}

		element.AddRefRGNodes ();
	}

	m_pRgn->RefreshAttributes (g_map);
	if (! m_bWasModified)
		m_pRgn->dwFlags &= ~rgn_t::fModified;
}

void CDeletePointAction::Redo () {
	CDeletePointAction::Do ();
}

void DeletePoint (rgn_t * _pRgn, size_t _cElement, size_t _cPointIndex) {
	assert (_pRgn);
	assert (_cElement < _pRgn->elements.size ());
	assert (_cPointIndex < _pRgn->elements [_cElement].points.size ());

	PerformEditAction (new CDeletePointAction (_pRgn, _cElement, _cPointIndex));

	Refresh ();
}
